import { reactive, ref } from 'vue'
import { usePopupDialogStore } from '@/store/popup-dialog'
import { usePopupMessageStore } from '@/store/popup-message'
import { $t } from '@/i18n'
import { notifyBuy, notifySub } from '@/utils/state-broadcast'
import { toQueryString } from '@/utils/string-helper'
import { CONSUME_TYPE } from '@/constant'
import { trackEvent } from '@/gtm'
import API from '@/http'
import { sendDonateMessage } from '@/ws'

export function usePayment() {
  const { open } = usePopupMessageStore()
  const { close } = usePopupDialogStore()

  const frequency = 5000 // 5 秒輪詢一次
  const timeout = 1000 * 60 * 5 // 5 分鐘
  let tick = 0

  const isContinue = ref(true)
  let window

  const actions = reactive({
    onSuccess: null,
    onFailure: null,
    onCancel: null,
  })

  const setupAction = ({ onSuccess, onFailure, onCancel }) => {
    actions.onSuccess = onSuccess
    actions.onFailure = onFailure
    actions.onCancel = onCancel
  }

  /**
   * 支付程序
   * @param {Function} api
   * @param {Object} data Payload
   * @param {Number} paymentType 打賞, 訂閱, 購買商品, 聊天商品, 聊天訂閱
   * @param {Window} newWindow 視窗物件
   * @param {String} userUUID 用戶 UUID
   * @param {Number} amount 金額
   * @param {Function} onSuccess 成功回調
   * @param {Function} onFailure 失敗回調
   * @param {Function} onCancel 取消回調
   * @param {Object} extraData 額外資料, 通常用於付款後解鎖
   * @see {@link CONSUME_TYPE} 支付類型
   */
  async function pay({
    api,
    checkApi,
    data,
    paymentType,
    newWindow,
    userUUID = null,
    amount,
    onSuccess = null,
    onFailure = null,
    onCancel = null,
    extraData = {},
  } = {}) {
    try {
      window = newWindow
      setupAction({ onSuccess, onFailure, onCancel })

      if (import.meta.env.DEV) data = { ...data, is_test: 'dev_server_test' }
      const response = await api({ data })

      if (!response.order_no || !response.pay_url) {
        throw new Error('Order number or payment callback url is missing')
      }

      window.location = response.post_data
        ? `/payment.html?${toQueryString({ ...response.post_data, POST_URL: encodeURIComponent(response.pay_url) })}`
        : response.pay_url

      const gtmData = getGtmData(paymentType, data)
      trackEvent({ key: gtmData.id, value: amount, ...gtmData.meta })

      isContinue.value = true
      fetchPollingResult({
        api: checkApi,
        orderNo: response.order_no,
        payload: data,
        paymentType,
        userUUID,
        gtmData,
        extraData,
      })
    } catch (e) {
      console.error('[Payment Error]', e)
      setTimeout(() => {
        window.close()
        close()
        open($t('label.paymentFailed') + e)
      }, 1500)
    }
  }

  /**
   * 輪詢支付結果
   * @param {Function} api
   * @param {String} orderNo 訂單編號
   */
  const fetchPollingResult = async ({ api, orderNo, payload, paymentType, userUUID, gtmData, extraData } = {}) => {
    console.log('[fetchPollingResult] isContinue.value', isContinue.value)
    if (!isContinue.value) {
      console.log('[fetchPollingResult] Continue is false, stop polling')
      isContinue.value = true
      return
    }
    console.log('[fetchPollingResult] Continue is true, polling...')
    try {
      if (tick >= timeout) {
        setTimeout(() => {
          close()
          open($t('payment.failed'))
          trackEvent({ key: 48, ...gtmData })
        }, 1500)
        return
      }
      const response = await api({ data: { order_no: orderNo } })
      console.log('[fetchPollingResult] Polling Result', response)
      console.log('[fetchPollingResult] MODE', import.meta.env.MODE)

      if (response.success) {
        close()
        open($t('payment.success'))
        trackEvent({ key: 47, ...gtmData })

        actions.onSuccess && actions.onSuccess()
        actionAfterSuccess({ paymentType, payload, userUUID, extraData })
      } else {
        console.log('[fetchPollingResult] Polling Pending...')
        setTimeout(() => {
          fetchPollingResult({
            api,
            orderNo,
            payload,
            paymentType,
            userUUID,
            gtmData,
          })
          tick += frequency
        }, frequency)
      }
    } catch (e) {
      trackEvent({ key: 48, ...gtmData })
      actions.onFailure && actions.onFailure()
      console.error('[fetchPollingResult] Polling Failed...', e)
      isContinue.value = false
      setTimeout(() => {
        window.close()
        close()
        open($t('label.paymentFailed') + e)
      }, 1500)
    }
  }

  const getGtmData = (paymentType, payload) => {
    const gtmDataMap = {
      [CONSUME_TYPE.REWARD]: { id: 46, meta: { aff: payload.aff } },
      [CONSUME_TYPE.SUBSCRIBE]: { id: 44, meta: { aff: payload.author_aff } },
      [CONSUME_TYPE.SHOP_BUY]: { id: 45, meta: { id: payload.id } },
      [CONSUME_TYPE.UNLOCK]: { id: 50, meta: {} },
      [CONSUME_TYPE.CHAT_BUY]: { id: 45, meta: { id: payload.id } },
      [CONSUME_TYPE.CHAT_SUBSCRIBE]: { id: 44, meta: { id: payload.id } },
      [CONSUME_TYPE.PREMIUM]: { id: 50, meta: {} },
    }
    if (gtmDataMap[paymentType]) return gtmDataMap[paymentType]
    throw new Error('Payment Type Error')
  }

  const cancel = () => {
    isContinue.value = false
    if (window) window.close()
    open($t('payment.cancel'))
    actions.onCancel && actions.onCancel()
  }

  const payStripe = async ({
    data,
    paymentType,
    userUUID = null,
    amount,
    paymentMethod,
    onSuccess = null,
    onFailure = null,
    onCancel = null,
    extraData = {},
  }) => {
    setupAction({ onSuccess, onFailure, onCancel })

    const payload = {
      to_aff: data.author_aff,
      type: paymentType,
      user_payment_method_id: paymentMethod,
      amount,
    }

    if (data.id) payload.m_id = data.id
    if (data.message) payload.msg = data.message

    try {
      const response = await API.Payment.offSessionPay({ data: payload })
      if (!response.order_id) throw new Error('Order number is missing')

      const gtmData = getGtmData(paymentType, data)
      trackEvent({ key: gtmData.id, value: amount, ...gtmData.meta })

      isContinue.value = true
      fetchPollingResult({
        api: API.Payment.checkPayResult,
        orderNo: response.order_id,
        payload: data,
        paymentType,
        userUUID,
        gtmData,
        extraData,
      })
    } catch (e) {
      setTimeout(() => {
        close()
        open($t('label.paymentFailed') + e)
      }, 1500)
    }
  }

  const actionAfterSuccess = ({ paymentType, payload, userUUID, extraData = {} }) => {
    // 廣播通知
    switch (paymentType) {
      case CONSUME_TYPE.REWARD:
        sendDonateMessage(
          payload.message || import.meta.env.VITE_DONATE_DEFAULT_MESSAGE,
          userUUID,
          payload.aff,
          payload.amount,
        )
        break
      case CONSUME_TYPE.SUBSCRIBE:
        notifySub(payload.author_aff)
        break
      case CONSUME_TYPE.SHOP_BUY:
        notifyBuy(payload.id)
        break
      case CONSUME_TYPE.UNLOCK:
        // notifyCampaign()
        break
      case CONSUME_TYPE.CHAT_BUY:
        extraData && extraData.media_list.forEach((media) => notifyBuy(media.id))
        break
      case CONSUME_TYPE.CHAT_SUBSCRIBE:
        extraData && notifySub(extraData.author.aff)
        break
      case CONSUME_TYPE.PREMIUM:
        console.log('Premium Buy Success')
        break
      default:
        throw new Error('Payment Type Error')
    }
  }

  return { pay, payStripe, cancel }
}
